use32
org 0

macro print_digit v* {
  local a
  a = (v and $F) + '0'
  if a > '9'
    a = a + ('A' - '9' - 1)
  end if
  db a
}
macro print_word v* {
  local a
  a = 7
  while a >= 0
    print_digit (v shr (4*a))
    a = a-1
  end while
}
macro print_halfword v* {
  local a
  a = 3
  while a >= 0
    print_digit (v shr (4*a))
    a = a-1
  end while
}
macro print_byte v* {
  local a
  a = 1
  while a >= 0
    print_digit (v shr (4*a))
    a = a-1
  end while
}

_zero = 0
_at = 1

_v0 = 2
_v1 = 3

_a0 = 4
_a1 = 5
_a2 = 6
_a3 = 7

_t0 = 8
_t1 = 9
_t2 = 10
_t3 = 11
_t4 = 12
_t5 = 13
_t6 = 14
_t7 = 15

_s0 = 16
_s1 = 17
_s2 = 18
_s3 = 19
_s4 = 20
_s5 = 21
_s6 = 22
_s7 = 23

_t8 = 24
_t9 = 25

_k0 = 26
_k1 = 27

_gp = 28
_sp = 29
_fp = 30
_ra = 31

macro _register_name n {
if n = _zero
  db '$zero'
else if n = _at
  db '$at'
else if n >= _v0 & n <= _v1
  db '$v', (n-_v0+'0')
else if n >= _a0 & n <= _a3
  db '$a', (n-_a0+'0')
else if n >= _t0 & n <= _t7
  db '$t', (n-_t0+'0')
else if n >= _s0 & n <= _s7
  db '$s', (n-_s0+'0')
else if n >= _t8 & n <= _t9
  db '$t', (n-_t8+'8')
else if n >= _k0 & n <= _k1
  db '$k', (n-_k0+'0')
else if n = _gp
  db '$gp'
else if n = _sp
  db '$sp'
else if n = _fp
  db '$fp'
else if n = _ra
  db '$ra'
end if
}

macro __clear {
  _a=0
  _b=0
  _c=0
  _d=0
  _e=0
  _f=0 }
macro __opcode _  { _a = (_ and 0111111b)   shl (32-6) }
macro __rs _      { _b = (_ and 011111b)    shl (32-6-5) }
macro __rt _      { _c = (_ and 011111b)    shl (32-6-5-5) }
macro __rd _      { _d = (_ and 011111b)    shl (32-6-5-5-5) }
macro __shamt _   { _e = (_ and 011111b)    shl (32-6-5-5-5-5) }
macro __func _    { _f = (_ and 0111111b) }
macro __imm _     { _d = (_ and 0xFFFF) }
macro __addr _    { _b = (_ and 0x3FFFFFF) }

; 1111 1111 1111 1111 - 1111 1111 1111 1111
;    F    F    F    F -    F    F    F    F
; 1111 1111 1111 1111 - 1111 1111 1111 1111
; 32   28   24   20     16   12   8    4
; 1111 1111 1111 1111 - 1111 1111 1111 1111
; ^ 3  F^ opcode
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;        ^ 1F ^ rs
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;        ^ 3   F    F      F    F    F   F^ address
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;              ^ 1F ^ rt
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;                       ^ 1F ^ rd
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;                       ^  F    F    F   F^ immediate
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;                             ^ 1F ^ shamt
; 1111 1111 1111 1111 - 1111 1111 1111 1111
;                                   ^ 3  F^ func

; Print registers' names
macro print_instr_reg _name*, [_reg] {
common
  db '  ',_name
forward
  db ' '
  _register_name _reg
  db ','
common
  store byte 10 at $-1
}

; Print registers then an immediate
macro print_instr_rimm _name*, _imm*, [_reg] {
common
  db '  ',_name
forward
  db ' '
  _register_name _reg
  db ','
common
if _imm <= $FF
  db ' 0x'
  print_byte _imm
else if _imm <= $FFFF
  db ' 0x'
  print_halfword _imm
else
  ;print_word _imm
  ;db ' # ',`_imm
  db ' ',`_imm
end if
  db 10
}

; Print an address
macro print_instr_addr _name*, _addr* {
  db '  ',_name,' '
  db `_addr
  db 10
}

; Print $X, imm($Y)
macro print_instr_rrel _name*, _rt*, _imm*, _rs* {
  db '  ',_name,' '
  _register_name _rt
  db ', 0x'
if _imm <= $FF
  print_byte _imm
else if _imm <= $FFFF
  print_halfword _imm
else
  ;print_word _imm
  db ' # ',`_imm
end if
  db '('
  _register_name _rs
  db ')',10
}

macro R _rs*, _rt*, _rd*, _shamt*, _func* {
  __clear
  __opcode 0
  __rs _rs
  __rt _rt
  __rd _rd
  __shamt _shamt
  __func _func
virtual defaultSpace
  print_word (_a+_b+_c+_d+_e+_f)
end virtual
}
;_add _t0, _t1, _t2 -> R _t1, _t2, _t0, 0, 32 -> 0x012A4020
; Unsigned left shift / logical left shift
macro _sll _rd*, _rt*, _shamt* {
  print_instr_rimm 'sll',_shamt, _rd, _rt
  R 0, _rt, _rd, _shamt, 0 }
; Unsigned right shift / logical right shift
macro _srl _rd*, _rt*, _shamt* {
  print_instr_rimm 'srl',_shamt, _rd, _rt
  R 0, _rt, _rd, _shamt, 2 }
; Signed right shift / arithmetic right shift
macro _sra _rd*, _rt*, _shamt* {
  print_instr_rimm 'sra',_shamt, _rd, _rt
  R 0, _rt, _rd, _shamt, 3 }
; Unsigned left shift with register
macro _sllv _rd*, _rt*, _rs* {
  print_instr_reg 'sllv', _rd, _rt, _rs
  R _rs, _rt, _rd, 0, 4 }
; Unsigned right shift with register
macro _srlv _rd*, _rt*, _rs* {
  print_instr_reg 'srlv', _rd, _rt, _rs
  R _rs, _rt, _rd, 0, 6 }
; Signed right shift with register
macro _srav _rd*, _rt*, _rs* {
  print_instr_reg 'srav', _rd, _rt, _rs
  R _rs, _rt, _rd, 0, 7 }
; Jump to register
macro _jr _rs* {
  print_instr_reg 'jr', _rs
  R _rs, 0, 0, 0, 8 }
; Jump and link to registers
macro _jalr _rd=_ra, _rs* {
if _rd=_ra
  print_instr_reg 'jalr', _rs
else
  print_instr_reg 'jalr', _rd, _rs
end if
  R _rs, 0, _rd, 0, 9 }
; Syscall
macro _syscall {
  _asm '  syscall'
  R 0, 0, 0, 0, 12 }
; Move from hi
macro _mfhi _rd* {
  print_instr_reg 'mfhi', _rd
  R 0, 0, _rd, 0, 16 }
; Move to hi
macro _mthi _rs* {
  print_instr_reg 'mthi', _rs
  R _rs, 0, 0, 0, 17 }
; Move from lo
macro _mflo _rd* {
  print_instr_reg 'mflo', _rd
  R 0, 0, _rd, 0, 18 }
; Move to lo
macro _mtlo _rs* {
  print_instr_reg 'mtlo', _rs
  R _rs, 0, 0, 0, 19 }
; Signed multiplication
macro _mult _rs*, _rt* {
  print_instr_reg 'mult', _rs, _rt
  R _rs, _rt, 0, 0, 24 }
; Unsigned multiplication
macro _multu _rs*, _rt* {
  print_instr_reg 'multu', _rs, _rt
  R _rs, _rt, 0, 0, 25 }
; Signed division
macro _div _rs*, _rt* {
  print_instr_reg 'div', _rs, _rt
  R _rs, _rt, 0, 0, 26 }
; Unsigned division
macro _divu _rs*, _rt* {
  print_instr_reg 'divu', _rs, _rt
  R _rs, _rt, 0, 0, 27 }
; Add signed / add and raise exception on signed overflow
macro _add _rd*, _rs*, _rt* {
  print_instr_reg 'add', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 32 }
; Add unsigned
macro _addu _rd*, _rs*, _rt* {
  print_instr_reg 'addu', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 33 }
; Subtract signed / subtract and raise exception on signed overflow
macro _sub _rd*, _rs*, _rt* {
  print_instr_reg 'sub', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 34 }
; Subtract unsigned
macro _subu _rd*, _rs*, _rt* {
  print_instr_reg 'subu', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 35 }
macro _and _rd*, _rs*, _rt* {
  print_instr_reg 'and', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 36 }
macro _or _rd*, _rs*, _rt*  {
  print_instr_reg 'or', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 37 }
macro _xor _rd*, _rs*, _rt* {
  print_instr_reg 'xor', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 38 }
macro _nor _rd*, _rs*, _rt* {
  print_instr_reg 'nor', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 39 }
; Signed compare rs < rt
macro _slt _rd*, _rs*, _rt* {
  print_instr_reg 'slt', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 42 }
; Unsigned compare rs < rt
macro _sltu _rd*, _rs*, _rt* {
  print_instr_reg 'sltu', _rd, _rs, _rt
  R _rs, _rt, _rd, 0, 43 }
macro _move _rt*, _rs* {
  ;db '  # move '
  ;_register_name _rt
  ;db ', '
  ;_register_name _rs
  ;db 10
  _addu _rt, _zero, _rs }
macro _nop {
virtual defaultSpace
  dd 0
end virtual
  db 'nop',10
}
macro J _op*, _addr* {
  __clear
  __opcode _op
  __addr _addr
virtual defaultSpace
  print_word (_a+_b)
end virtual
}
macro _j _addr* {
  print_instr_addr 'j', _addr
  J 2, (_addr shr 2) }
macro _jal _addr* {
  print_instr_addr 'jal', _addr
  J 3, (_addr shr 2) }

macro I _op*, _rs*, _rt*, _imm* {
  __clear
  __opcode _op
  __rs _rs
  __rt _rt
  __imm _imm
virtual defaultSpace
  print_word (_a+_b+_c+_d)
end virtual
}
macro _beq _rs*, _rt*, _imm* {
  print_instr_rimm 'beq', _imm, _rs, _rt
  I 4, _rs, _rt, (_imm shr 2) }
macro _bne _rs*, _rt*, _imm* {
  print_instr_rimm 'bne', _imm, _rs, _rt
  I 5, _rs, _rt, (_imm shr 2) }
;Branch if less or equal/greater than zero
macro _blez _rs*, _imm* {
  print_instr_rimm 'blez', _imm, _rs
  I 6, _rs, 0, (_imm shr 2) }
macro _bgtz _rs*, _imm* {
  print_instr_rimm 'bgtz', _imm, _rs
  I 7, _rs, 0, (_imm shr 2) }
; Add immediate
macro _addi _rt*, _rs*, _imm* {
  print_instr_rimm 'addi', _imm, _rt, _rs
  I 8, _rs, _rt, _imm }
macro _addiu _rt*, _rs*, _imm* {
  print_instr_rimm 'addiu', _imm, _rt, _rs
  I 9, _rs, _rt, _imm }
macro _subi _rt*, _rs*, _imm* {
  _li _at, _imm
  _sub _rt, _rs, _at }
macro _subiu _rt*, _rs*, _imm* {
  _li _at, _imm
  _subu _rt, _rs, _at }
; Comparision with immediate
macro _slti _rt*, _rs*, _imm* {
  print_instr_rimm 'slti', _imm, _rt, _rs
  I 10, _rs, _rt, _imm }
macro _sltiu _rt*, _rs*, _imm* {
  print_instr_rimm 'sltiu', _imm, _rt, _rs
  I 11, _rs, _rt, _imm }
; Logic with constants
macro _andi _rt*, _rs*, _imm* {
  print_instr_rimm 'andi', _imm, _rt, _rs
  I 12, _rs, _rt, _imm }
macro _ori _rt*, _rs*, _imm* {
  print_instr_rimm 'ori', _imm, _rt, _rs
  I 13, _rs, _rt, _imm }
macro _xori _rt*, _rs*, _imm* {
  print_instr_rimm 'xori', _imm, _rt, _rs
  I 14, _rs, _rt, _imm }
; Load from main memory
macro _lui _rt*, _imm* {
  print_instr_rimm 'lui', _imm, _rt
  I 15, 0, _rt, _imm }
macro _lb _rt*, _imm=0, _rs* {
  print_instr_rrel 'lb', _rt, _imm, _rs
  I 32, _rs, _rt, _imm }
macro _lh _rt*, _imm*, _rs* {
  print_instr_rrel 'lh', _rt, _imm, _rs
  I 33, _rs, _rt, _imm }
macro _lw _rt*, _imm*, _rs* {
  print_instr_rrel 'lw', _rt, _imm, _rs
  I 34, _rs, _rt, _imm }
macro _lbu _rt*, _imm*, _rs* {
  print_instr_rrel 'lbu', _rt, _imm, _rs
  I 36, _rs, _rt, _imm }
macro _lhu _rt*, _imm*, _rs* {
  print_instr_rrel 'lhu', _rt, _imm, _rs
  I 37, _rs, _rt, _imm }
macro _li _rt*, _imm32* {
;  db '  # li '
;  _register_name _rt
;  db ', 0x'
;if _imm32 <= $FF
;  print_byte _imm32
;else if _imm32 <= $FFFF
;  print_halfword _imm32
;else
;  print_word _imm32
;end if
;  db ' # ',`_imm32
;  db 10
if _imm32 > 0xFFFF
  _lui _rt, (_imm32 shr 16)
  if (_imm32 and 0xFFFF) > 0
    _ori _rt, _rt, (_imm32 and 0xFFFF)
  end if
else
  _addiu _rt, _zero, _imm32
end if
}
macro _la _rt*, _imm32* {
;  db '  # la '
;  _register_name _rt
;  db ', 0x'
;  print_word _imm32
;  db ' # ',`_imm32
;  db 10
if _imm32 > 0xFFFF
  _lui _rt, (_imm32 shr 16)
  if (_imm32 and 0xFFFF) > 0
    _ori _rt, _rt, (_imm32 and 0xFFFF)
  end if
else
  _addiu _rt, _zero, _imm32
end if
}
; Store on main memory
macro _sb _rt*, _imm*, _rs* {
  print_instr_rrel 'sb', _rt, _imm, _rs
  I 40, _rs, _rt, _imm }
macro _sh _rt*, _imm*, _rs* {
  print_instr_rrel 'sh', _rt, _imm, _rs
  I 41, _rs, _rt, _imm }
macro _sw _rt*, _imm*, _rs* {
  print_instr_rrel 'sw', _rt, _imm, _rs
  I 43, _rs, _rt, _imm }

; SPIM macros

defaultSpace equ _Text
macro _asm text {
  db text,10
}
macro _noat {
  _asm '.set noat'
}
macro _at {
  _asm '.set at'
}
macro _comment text {
  db '# ',text,10
}
macro _comment_append text {
  store byte ' ' at $-1
  db '# ',text,10
}
virtual at 0x00400024
  _Text::
end virtual

macro _ttext {
  local addr
virtual _Text
  addr:
end virtual
  _addr = addr
  db '.text 0x'
  print_word _addr
  db 10
  purge _addr
  defaultSpace equ _Text
}

macro _dtext {
  local addr
virtual _Text
  addr:
end virtual
  _addr = addr
  db '.data 0x'
  print_word _addr
  db 10
  purge _addr
  defaultSpace equ _Text
}
macro _globl _lbl {
  db '.globl ',`_lbl,10
}
virtual at 0x10010000
  _Data::
end virtual

macro _ddata {
  local addr
virtual _Data
  addr:
end virtual
  _addr = addr
  db '.data 0x'
  print_word _addr
  db 10
  purge _addr
  defaultSpace equ _Data
}
macro _word [v] {
common
  virtual defaultSpace
forward
  dd v
common
  end virtual
forward
  db '  .word 0x'
  print_word v
  db 10
}
macro _dw [v] {
  _word v
}
macro _half [v] {
common
  virtual defaultSpace
forward
  dw v
common
  end virtual
forward
  db '  .half 0x'
  print_halfword v
  db 10
}
macro _dh [v] {
  _half v
}
macro _byte [v] {
common
  virtual defaultSpace
forward
  db v
common
  end virtual
forward
  db '  .byte 0x'
  print_byte v
  db 10
}
macro _db [v] {
  _byte v
}
macro _asciiz t {
  local last
virtual defaultSpace
  db t,0
end virtual
  db '  .asciiz "'
  db t
  load last byte from $-1
if last = 10
  store byte '\' at $-1
  db 'n'
end if
  db '"',10
}
macro _space len {
  db '.space 0x'
  print_halfword len
  db 10
virtual defaultSpace
  rb len
end virtual
}
macro _rw len {
  _space (len*4)
}
macro _rh len {
  _space (len*2)
}
macro _rb len {
  _space len
}
macro _label name {
  _asm <`name,':'>
virtual defaultSpace
  name:
end virtual
}
macro _align expoent {
  _asm <'.align ',`expoent>
virtual defaultSpace
  align (2 shl (expoent-1))
end virtual
}
; SPIM syscalls
spim_service:
  .puti = 1
  .putf = 2
  .putd = 3
  .puts = 4
  .geti = 5
  .getf = 6
  .getd = 7
  .gets = 8
  .sbrk = 9
  .exit = 10
  .putc = 11
  .getc = 12
  .open = 13
  .read = 14
  .write = 15
  .close = 16
  .exit2 = 17
spim_service.filemode.r = 0
spim_service.filemode.w = 1
